﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Reflection;
using System.Globalization;
using System.Text;

using Kenly.DBFramework;
using Kenly.DBFramework.Reflection;

namespace Kenly.DBFramework.Gateway
{
    /// <summary>
    ///  AggregateEntity to execute aggregate calculations in database.
    /// </summary>
    public class AggregateEntity<T> : IAggregateEntity where T : AggregateEntity<T>, new()
    {
        private static Order _order = new Order();

        /// <summary>
        /// The order to sort the result of a query.
        /// </summary>
        protected static Order SortedOrder
        {
            get
            {
                return _order;
            }
            set
            {
                _order = value;
            }
        }

        /// <summary>
        /// Gets config from ConfigFactory.
        /// </summary>
        protected static GatewayConfig Config
        {
            get
            {
                return ConfigManager.FindConfig(typeof(T));
            }
        }

        /// <summary>
        /// Specifies the DatabaseType and connectionString.
        /// </summary>
        /// <param name="dbType"></param>
        /// <param name="connectionString"></param>
        //protected static void InitializeGateway(DatabaseType dbType, string connectionString)
        //{
        //    if (string.IsNullOrEmpty(connectionString))
        //    {
        //        throw new GatewayException(MsgResource.ConnectionStringMissing);
        //    }

        //    TableAttribute tableAttribute = AttributeUtility.GetTableAttribute(typeof(T));
        //    if (tableAttribute == null)
        //    {
        //        throw new GatewayException(MsgResource.InvalidEntityConfig);
        //    }

        //    _config.DatabaseType = dbType;
        //    _config.ConnectionString = connectionString;

        //    _commandExecutor = CreateCommandExecutor();
        //}

        /// <summary>
        /// 
        /// </summary>
        /// <param name="config"></param>
        //protected static void InitializeGateway(GatewayConfig config)
        //{
        //    if (config == null || string.IsNullOrEmpty(config.ConnectionString))
        //    {
        //        throw new ConnectionException(MsgResource.ConnectionStringMissing);
        //    }
        //    _config = config;
        //}

        private static IViewCommandExecutor CreateCommandExecutor()
        {
            var currentConfig = Config;

            IDatabase database = DatabaseFactory.CreateInstance(currentConfig.DatabaseType);
            database.ConnectionString = currentConfig.ConnectionString;
            database.Command.CommandTimeout = currentConfig.CommandTimeout;
            return database.CreateViewCommandExecutor();
        }

        private static void ClearOrder()
        {
            if (_order != null)
            {
                _order.Clear();
            }
        }



        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public static List<T> Execute()
        {
            return EntityMapper<T>.ToEntities(ExecuteDataTable());
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="condition"></param>
        /// <returns></returns>
        protected static List<T> Execute(string condition)
        {
            return EntityMapper<T>.ToEntities(ExecuteDataTable(condition));
        }

        /// <summary>
        /// Executes a aggregate sql statement ,and returns a DataTable.
        /// </summary>
        /// <returns></returns>
        public static DataTable ExecuteDataTable()
        {
            return ExecuteDataTable(" 1=1 ");
        }

        /// <summary>
        /// Executes a aggregate sql statement ,and returns a DataTable.
        /// </summary>
        /// <param name="condition"></param>
        /// <returns></returns>
        protected static DataTable ExecuteDataTable(string condition)
        {
            DataTable dt = new DataTable();

            List<AggregateAttribute> aggregateAttributes = new List<AggregateAttribute>();
            List<GroupingAttribute> groupingAttributes = new List<GroupingAttribute>();

            GetAttributes(ref aggregateAttributes, ref groupingAttributes);
            if (aggregateAttributes.Count <= 0)
            {
                return dt;
            }

            string sql = AggregateCommandReflector.GroupSql(typeof(T), aggregateAttributes, groupingAttributes, 
                condition,SortedOrder);

            var commandExecutor = CreateCommandExecutor();
            dt = commandExecutor.ExecuteDataTable(sql, false);

            return dt;
        }

       /// <summary>
       /// 
       /// </summary>
       /// <param name="aggregateAttributes"></param>
       /// <param name="groupingAttributes"></param>
        private static void GetAttributes(ref List<AggregateAttribute> aggregateAttributes,ref List<GroupingAttribute> groupingAttributes)
        {
            PropertyInfo[] propertyInfos = typeof(T).GetProperties();
            foreach (PropertyInfo propertyInfo in propertyInfos)
            {
                GetAggregateAttribute(propertyInfo, ref aggregateAttributes);
                GetGroupingAttribute(propertyInfo, ref groupingAttributes);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="propertyInfo"></param>
        /// <param name="aggregateAttributes"></param>
        private static void GetAggregateAttribute(PropertyInfo propertyInfo, ref List<AggregateAttribute> aggregateAttributes)
        {
            AggregateAttribute aggregateAttribute = AttributeUtility.GetCustomAttribute<AggregateAttribute>(propertyInfo);
            if (aggregateAttribute == null)
            {
                return;
            }

            aggregateAttribute.PropertyName = propertyInfo.Name;
            aggregateAttributes.Add(aggregateAttribute);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="propertyInfo"></param>
        /// <param name="groupingAttributes"></param>
        private static void GetGroupingAttribute(PropertyInfo propertyInfo, ref List<GroupingAttribute> groupingAttributes)
        {
            GroupingAttribute groupingAttribute = AttributeUtility.GetCustomAttribute<GroupingAttribute>(propertyInfo);
            if (groupingAttribute == null)
            {
                return;
            }

            groupingAttribute.PropertyName = propertyInfo.Name;
            groupingAttributes.Add(groupingAttribute);
        }


       

    }

}
